﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Engine.MarchCube
{
    public struct IBVB
    {
        public short[] IB;
        public VertexPositionColor[] VB;
    }
    public class Element
    {
        byte m_type;
        public byte Type { get { return m_type; } set { m_type = value; } }
        /// <summary>
        /// Построение меша
        /// </summary>
        /// <param name="GrindSize">Размер детализации сетки Кратный степени 2</param>
        /// <param name="Corners"> масив Елементов вокруг текущего элемента  
        ///         Y^
        ///          |
        ///          |
        ///          | 
        ///          6____15_____24
        ///         /|          /|
        ///        7 |  16     25|
        ///       /  3    12  /  21 
        ///      8_ _|17_____26  |
        ///      | 4 |  13   | 22|
        ///      |   0-----9-|---18------------>X 
        ///      5  / 14     23 / 
        ///      | 1    10   | 19 
        ///      |/          |/ 
        ///      2____11_____20
        ///     /
        ///    / 
        ///  Z/  
        /// 
        /// 
        /// </param>
        /// <param name="pos">положение элемента</param>
        public unsafe TRIANGLE[] Bild(int GrindSize,
            Element[] Corners, Vector3 pos)
        {
            if (GrindSize < 2)
            {
                GrindSize = 2;
            }
            List<TRIANGLE> tempList=new List<TRIANGLE>();           
            TRIANGLE[] tri = null;           
            double[, ,] val = new double[GrindSize +1, GrindSize +1, GrindSize +1];
            val[GrindSize / 2, GrindSize / 2, GrindSize / 2] = 1;
            CalcCorners(Corners, ref val, GrindSize +1);
            CalcAllLevel(ref val, GrindSize +1);
            float sh = 1f / GrindSize;
            for (int x = 0; x < GrindSize; x++)
            {
                for (int y = 0; y < GrindSize; y ++)
                {
                    for (int z = 0; z < GrindSize; z ++)
                    {
                        GRIDCELL gc = MarchingCubes.GetBaseGrid(new Vector3(pos.X + x*sh, pos.Y + y*sh, pos.Z + z*sh));
                        for (int i = 0; i < 8; i++)
                        {
                           
                            gc.val[i] = GetVal(ref val,x, y , z , i);
                           
                        }
                        tri = null;
                        int count = Engine.MarchCube.MarchingCubes.Polygonise(gc, 0.2f, ref tri);

                        if (tri != null)
                        {                            
                            for (int i = 0; i < tri.Length; i++)
                            {
                                tempList.Add(tri[i]);                                
                            }
                        }
                    }
                }
            }

            return tempList.ToArray();
        }
        public static Color GetColor(byte _type)
        {
            Color tmp = Color.Transparent;
            switch (_type)
            {
                case 1:
                    tmp= Color.Green;
                    break;
                case 2:
                    tmp= Color.WhiteSmoke;
                    break;
                default:
                    break;
            }
            return tmp;
        }
        private float GetVal(ref double[, ,] val,int x, int y, int z, int i)
        {
            switch (i)
            {
                case 0:
                    return (float)(val[x, y, z]);
                case 1:
                    return (float)(val[x + 1, y, z]);
                case 2:
                    return (float)(val[x + 1, y + 1, z]);
                case 3:
                    return (float)(val[x, y + 1, z]);
                case 4:
                    return (float)(val[x, y, z + 1]);
                case 5:
                    return (float)(val[x + 1, y, z + 1]);
                case 6:
                    return (float)(val[x + 1, y + 1, z + 1]);
                case 7:
                    return (float)(val[x, y + 1, z + 1]);
            }
            return 0;
        }
        private void CalcAllLevel(ref double[, ,] val, int GrindSize)
        {
            int subgrind = 1;
            while (GrindSize -1 != Math.Pow(2, subgrind))
            {
                subgrind++;
            }

            for (int i = 1; i <= subgrind; i++)
            {

                int shag =(GrindSize-1)/ (int)Math.Pow(2, i);
                for (int x = 0; x < GrindSize - 1; x += shag)
                {
                    for (int y = 0; y < GrindSize-1; y+=shag)
                    {
                        for (int z = 0; z < GrindSize - 1; z += shag)
                        {
                            if (val[x, y, z] == 0 && x>0 && y>0 && z>0)
                            {
                                val[x, y, z] = 0 +
                                    val[x +shag, y, z] / (((x +shag) -shag) * ((x +shag) - x) + ((y) - y) * ((y) - y) + ((z) - z) * ((z) - z)) +
                                    val[x -shag, y, z] / (((x -shag) -shag) * ((x -shag) - x) + ((y) - y) * ((y) - y) + ((z) - z) * ((z) - z)) +
                                    val[x, y +shag, z] / (((x) -shag) * ((x) - x) + ((y +shag) - y) * ((y +shag) - y) + ((z) - z) * ((z) - z)) +
                                    val[x, y -shag, z] / (((x) -shag) * ((x) - x) + ((y -shag) - y) * ((y -shag) - y) + ((z) - z) * ((z) - z)) +
                                    val[x, y, z +shag] / (((x) -shag) * ((x) - x) + ((y) - y) * ((y) - y) + ((z +shag) - z) * ((z +shag) - z)) +
                                    val[x, y, z -shag] / (((x) -shag) * ((x) - x) + ((y) - y) * ((y) - y) + ((z -shag) - z) * ((z -shag) - z)) +
                                    val[x +shag, y +shag, z +shag] / (((x +shag) -shag) * ((x +shag) - x) + ((y +shag) - y) * ((y +shag) - y) + ((z +shag) - z) * ((z +shag) - z)) +
                                    val[x -shag, y -shag, z -shag] / (((x -shag) -shag) * ((x -shag) - x) + ((y -shag) - y) * ((y -shag) - y) + ((z -shag) - z) * ((z -shag) - z)) +
                                    val[x + shag, y - shag, z + shag] / (((x + shag) - shag) * ((x + shag) - x) + ((y - shag) - y) * ((y - shag) - y) + ((z + shag) - z) * ((z + shag) - z)) +
                                    val[x - shag, y + shag, z - shag] / (((x - shag) - shag) * ((x - shag) - x) + ((y + shag) - y) * ((y + shag) - y) + ((z - shag) - z) * ((z - shag) - z)) +
                                    val[x - shag, y + shag, z + shag] / (((x - shag) - shag) * ((x - shag) - x) + ((y + shag) - y) * ((y + shag) - y) + ((z + shag) - z) * ((z + shag) - z)) +
                                    val[x + shag, y - shag, z - shag] / (((x + shag) - shag) * ((x + shag) - x) + ((y - shag) - y) * ((y - shag) - y) + ((z - shag) - z) * ((z - shag) - z)) +
                                    val[x + shag, y + shag, z - shag] / (((x + shag) - shag) * ((x + shag) - x) + ((y + shag) - y) * ((y + shag) - y) + ((z - shag) - z) * ((z - shag) - z)) +
                                    val[x - shag, y - shag, z + shag] / (((x - shag) - shag) * ((x - shag) - x) + ((y - shag) - y) * ((y - shag) - y) + ((z + shag) - z) * ((z + shag) - z)) +
                                    val[x +shag, y, z +shag] / (((x +shag) -shag) * ((x +shag) - x) + ((y) - y) * ((y) - y) + ((z +shag) - z) * ((z +shag) - z)) +
                                    val[x -shag, y, z -shag] / (((x -shag) -shag) * ((x -shag) - x) + ((y) - y) * ((y) - y) + ((z -shag) - z) * ((z -shag) - z)) +
                                    val[x, y +shag, z +shag] / (((x) -shag) * ((x) - x) + ((y +shag) - y) * ((y +shag) - y) + ((z +shag) - z) * ((z +shag) - z)) +
                                    val[x, y -shag, z -shag] / (((x) -shag) * ((x) - x) + ((y -shag) - y) * ((y -shag) - y) + ((z -shag) - z) * ((z -shag) - z)) +
                                    val[x +shag, y +shag, z] / (((x +shag) -shag) * ((x +shag) - x) + ((y +shag) - y) * ((y +shag) - y) + ((z) - z) * ((z) - z)) +
                                    val[x -shag, y -shag, z] / (((x -shag) -shag) * ((x -shag) - x) + ((y -shag) - y) * ((y -shag) - y) + ((z) - z) * ((z) - z))
                                    ;
                            }
                            if (val[x, y, z] ==double.NaN)
                            {
                                val[x, y, z] = 0;
                            }
                            if (double.IsInfinity(val[x, y, z]))
                            {
                                val[x, y, z] = 0;
                            }

                        }
                    }
                }
            }
        }

        public void CalcCorners(Element[] elements, ref double[, ,] Values, int GrindSize)
        {
            int shag = GrindSize / 3;
            for (int x = 0; x < 3; x++)
            {
                for (int y = 0; y < 3; y++)
                {
                    for (int z = 0; z < 3; z++)
                    {
                        int index = x * 9 + y * 3 + z;
                        if (index != 13)
                        {
                            if (elements[index] != null)
                            {
                                Values[x * shag, y * shag, z * shag] = 1;
                            }
                        }
                    }
                }
            }
        }
    }
}
